#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>

extern int _end;
struct buffer_head *start_buffer = (struct buffer_head *) &_end;
struct buffer_head *hash_table[NR_HASH];
static struct buffer_head *free_list;

int NR_BUFFERS = 0;

static inline void wait_on_buffer(struct buffer_head *bh)
{
    cli();
    while (bh->b_lock)
        sleep_on(&bh->b_wait);
    sti();
}

void invalidate_buffers(int dev)
{
    int i;
    struct buffer_head *bh;

    bh = start_buffer;
    for (i = 0; i < NR_BUFFERS; i++, bh++) {
        if (bh->b_dev != dev)
            continue;
        if (bh->b_dev == dev)   // 如果是指定的设备
            bh->b_uptodate = bh->b_dirt = 0;
    }
}

void check_disk_change(int dev)
{
    if (MAJOR(dev) != 2)
        return;
    if (!floppy_change(dev & 0x03))
        return;
    invalidate_buffers(dev);
}

struct buffer_head *bread(int dev, int block)
{
    int i;
    struct buffer_head *bh = free_list;
    bh->b_dev = dev;
    bh->b_blocknr = block;
    ll_rw_block(READ, bh);
    for (i = 0; i < 18; i++) {
        printk("%x ", (unsigned char) bh->b_data[i]);
    }
    printk("\r\n");
    wait_on_buffer(bh);
    if (bh->b_uptodate)
        return bh;
    return NULL;
}

void buffer_init(long buffer_end)
{
    struct buffer_head *h = start_buffer;
    void *b;
    int i;

    if (buffer_end == 1 << 20)
        b = (void *) (640 * 1024);
    else
        b = (void *) buffer_end;
    while ((b -= BLOCK_SIZE) >= ((void *) (h + 1))) {
        h->b_dev = 0;
        h->b_dirt = 0;
        h->b_count = 0;
        h->b_lock = 0;
        h->b_uptodate = 0;
        h->b_wait = NULL;
        h->b_next = NULL;
        h->b_prev = NULL;
        h->b_data = (char *) b;
        h->b_prev_free = h - 1;
        h->b_next_free = h + 1;
        h++;
        NR_BUFFERS++;
        if (b == (void *) 0x100000) // 0xA0000-0x100000是显存和BIOS ROM
            b = (void *) 0xA0000;   // 跳过这一段
    }
    h--;
    free_list = start_buffer;
    free_list->b_prev_free = h;
    h->b_next_free = free_list;
    for (i = 0; i < NR_HASH; i++)
        hash_table[i] = NULL;
}